API
接口待重构
调用远程服务会有3种结果:成功、失败与超时。
- 成功时会返回该请求的返回值,失败时则会返回错误码和错误信息。
- 注意,超时发生后,依然可能会有成功或失败事件被触发(比如刚超时就成功了。而我们不会因为超时就把一次成功的操作给回滚)。
- 当然,如果对应的远程服务遇到了崩溃,那么就会遇到只有超时,但没有成功或失败。
- 所以,超时通常只是脚本流程的一个保险,避免流程进行不下去。理论上我们会尽量避免超时发生。
- 通过注册
ok
、error
和timeout
事件可以获取请求结果。- 当然,如果你不关心请求结果,你可以不注册这些事件。
官方大厅图有读写其它图的各信息的权限(否则游戏的战报信息大厅就拿不到了)
积分、存档、道具、列表、货币、名字这几套会有统一的commit,以便消耗道具、修改积分、修改列表在同一次commit里;统计没有commit,操作了立即生效
- 只要在一次commit里提交的,都是一个事务,如有意外的话本次commit里的所有数据操作都将一起失败
- 不同系统里的索引(key)允许重复。甚至普通积分和数字积分也允许有重复的key
- 只要游戏结束前调用commit就算有效,即使在提交积分的时间里游戏已经结束。不过游戏结束后lua就收不到提交的反馈了,你只能希望他成功了
- 用户已经开启了游戏局的,大厅局对玩家的积分修改会失效,所以务必在申请开始游戏局前把所有要改的积分都commit
所有的超时时间,如果没有特殊说明,都是5秒
一秒内不允许做超过65536个查询或commit操作,否则回调会错乱。
API大体和服务器的积分系列api相同, 删除了一些安全性敏感的api
所有的写操作的target_map都强制指定为sce.s.readwrite_map 对于只读操作, 允许读取sce.s.readwrite_map和sce.s.readonly_map这两个地图的数据
需要特别注意, 客户端是32bit的应用程序, 所以文档内的所有integer都是32bit的, 这与服务端不同!
通用接口
get_commit
获得一个提交对象。除了统计,所有的修改操作都必须在提交对象上进行。可以把有关联的,必须同时成功的修改操作放在同一个提交对象里。
这是一个异步接口(积分系列的api大部分都是异步接口),也就是说,调用commit之后立刻去获取积分,你很可能取不到正确的值。实际上,即使积分服瞬间返回(实际上多少会有少量的延迟),commit的回调最快也要等当前语境的lua代码全都被执行完,才会被调用。之前没有接触过异步接口的人,千万不要以为ok的回调会先执行
由c++实现的api
local c = sce.s.get_commit()
commit
结束之前获得的提交对象,真正提交所有修改。在commit成功之前,所有相关修改未必真正的写入了数据库。提交需要一定的事件,通过注册ok
、error
和timeout
事件获得提交的结果。
所有操作会强制作用于地图sce.s.readwrite_map
- 参数
- desc (string) - 本次提交的描述。和提交相关联,在事后通过后台查看某些积分字段的修改记录时可以看到。
- events (table) - (可选) 响应事件
local c = sce.s.get_commit()
-- ...
-- 若干修改操作
-- ...
c.commit('比如转箱子',
{
ok = function ()
-- 提交成功
-- do some thing after success
end,
error = function (code, reason)
-- 提交失败
end,
timeout = function ()
-- 提交超时
end,
})
-- some other thing1
-- some other thing2
-- 不熟悉异步机制的请注意:some other thing1和some other thing2都会比do some thing after success先执行的
local c = sce.s.get_commit()
-- ...
-- 若干修改操作
-- ...
c.commit('增加地图专用货币', 'another_map',
{
ok = function ()
-- 提交成功
end,
error = function (code, reason)
-- 提交失败
end,
timeout = function ()
-- 提交超时
end,
})
score
积分
积分使用字符串作为索引(key),索引最长180。
积分分为普通积分、数字积分和字符串积分。数字积分必须是整数(int64范围),普通积分可以是任意类型,字符串,数字,表,都可以(但通常是用表)。对于普通积分来说,不管什么类型,均会在c++底层被序列化成二进制,二进制的长度不得超过64K。
普通积分、数字积分和字符串积分的索引允许重复。
将积分设置为nil
可以删除该积分,当你决定不再使用某项积分后,可以删除它。
当对积分进行操作时,并不会直接修改副本,而是将操作记录下来。当使用提交积分的功能提交操作记录后,才会根据操作记录更新副本。
!> 玩家离开游戏后,依然可以设置他的积分。但你最好不要这样做,因为该玩家可能会新开一局游戏,然后新老游戏局的同索引积分就会冲突。
score_init
初始化(读取)某玩家的积分。这是一个异步接口(积分系列的api大部分是异步接口),你得收到回调后才能去访问积分的值。
理论上,你可以不读取积分直接对积分进行写。
- 参数
- score_name (string) - (可选)地图积分名。用来查其它地图的积分(得有权限才让查)(对应地图表里没对应配置的时候默认就是地图名)。如果第一个参数是字符串,则表示是地图积分名。
- player (player/integer/nil) - 玩家。如果传nil,则表示使用客户端自己的userid。传integer的话则表示是userid。注意必须是大厅接口给返回的id,从Host接口返回的userid好像是虚拟的,这里不能用。如果传的是number,会被强转成integer;如果强转失败,则会当0用(假设userid < 1000 即为特殊userid, 可以记录一些全局数据, 比如记录boss被干掉了多少次,被干掉的次数越多会越强?)
- 注意当player == nil时, 服务端与客户端的处理方式是不同的
- events (table) - 响应事件
- 任意个key (string) - 要获取玩家的哪些key。如果一个key都不指定,则表示取该玩家的所有积分。
当积分初始化完成时,会根据初始化结果执行对应事件。如果使用该方法时积分已经初始化完毕,依然会再去数据库获取一次。并没有缓存,所以如无必要请避免反复调用该方法。
大厅服和匹配服的Lua脚本里可以获取到用户的部分积分,用于大厅和匹配的一些逻辑
由c++实现的api
-- 获取player的所有积分
sce.s.score_init(player,
{
ok = function (score, iscore, sscore)
-- 初始化完成。scores保存了普通积分,iscores保存了数字积分。
-- 有必要的话请自行保存这两个表
end,
error = function (code, reason)
-- 初始化失败
end,
timeout = function ()
-- 初始化超时
end,
})
--获取player2在AutoChess地图里的elo和ts这两项积分。不需要获取全部的情况下就少获取点,减轻服务器负担..
sce.s.score_init('AutoChess', player2,
{
ok = function (score, iscore, sscore)
-- 初始化完成。scores保存了普通积分,iscores保存了数字积分。
end,
error = function ()
-- 初始化失败
end,
timeout = function ()
-- 初始化超时
end,
}, 'elo', 'ts')
score_set
设置普通积分
-
参数
- player (player/integer/nil) - 玩家
- key (string) - 索引
- value (string/integer/table/nil) - 积分的值
-
返回
- ok (boolean) - 是否成功。目前失败的唯一可能就是table序列化后超过64K.
local c = sce.s.get_commit()
c.score_set(player, key, value)
-- .. 再改点别的什么
c.commit('描述')
score_seti
设置数字积分
- 参数
- player (player/integer/nil) - 玩家
- key (string) - 索引
- value (integer) - 积分的值
local c = sce.s.get_commit()
c.score_seti(player, key, value)
-- .. 再改点别的什么
c.commit('更新elo积分')
score_addi
累加数字积分。如果玩家的这项积分之前没有被设置过,那么假设之前是0。
- 参数
- player (player/integer/nil) - 玩家
- key (string) - 索引
- value (integer) - 积分的值
local c = sce.s.get_commit()
c.score_addi(player, key, value, map_name)
-- .. 再改点别的什么
c.commit('累加积分')
score_sets
设置字符串积分
- 参数
- player (player/integer/nil) - 玩家
- key (string) - 索引
- value (string) - 积分的值
local c = sce.s.get_commit()
c.score_sets(player, key, value)
-- .. 再改点别的什么
c.commit('更新elo积分')
money
货币
- 有若干种平台通用货币,若干种地图自定义货币
- 货币种类程序直接改数据库,后期api支持下,只允许后台访问
- 人民币只能充值成钻石(平台通用的人民币对应货币),否则充值界面不好展示
- 如果以后有单发特定地图的包,那可以只显示该地图的人民币对应货币(只能有一种人民币对应货币)
- 查余额、扣费、加钱(人民币对应货币不能在游戏局里加,只能在后台里加)
- 支持游戏内直接付钱(不转成货币)
- PC端弹二维码,网页端新窗口弹链接,手机端必须从渠道充,没有直接充这一说
- 和积分的区别,主要是有统一的充值扣款界面,可以用在统一做的商城里
money_init
初始化(读取)某玩家的所有货币的余额。货币初始化需要一段时间,请不要立即使用它。
理论上,你可以不读取货币余额,直接对该用户的货币进行增减。
由c++实现的api
- 参数
- player (player/integer/nil) - 玩家
- events (table) - 响应事件
当货币初始化完成时,会根据初始化结果执行对应事件。如果使用该方法时货币已经初始化完毕,依然会再去数据库获取一次。并没有缓存,所以如无必要请避免反复调用该方法。
-- 获取player的所有货币的余额
sce.s.money_init(player,
{
ok = function (moneys)
-- 初始化完成
end,
error = function (code, reason)
-- 初始化失败
end,
timeout = function ()
-- 初始化超时
end,
})
money_add
加钱
- 参数
- player (player/integer/nil) - 玩家
- money_name (string) - 货币的名字
- value (integer) - 加多少钱
local c = sce.s.get_commit()
c.money_add(player, money_name, value)
-- .. 再改点别的什么
c.commit('红包领取')
money_cost
扣钱
- 参数
- player (player/integer/nil) - 玩家
- money_name (string) - 货币的名字
- value (integer) - 扣多少钱
local c = sce.s.get_commit()
c.money_cost(player, money_name, value)
-- .. 再改点别的什么
c.commit('买装备')
name
名字
- 支持每地图每大区范围唯一起名 (没有大区概念前,就都默认在1区)
- 不存在才允许插入键值对,否则失败
- 支持以索引(key)的子串搜索
- 暂不支持拼音首字母的搜索
name_search
搜索包含指定字符串的所有名字
由c++实现的api
- 参数
- key (string) - 名字的索引
- name_substr (string) - 名字的子串
- events (table) - 响应事件
- 返回
- names (table) - 一个数组。数组的每一项有两个字段:
- name (string) - 名字。name必定包含name_substr。
- value (string) - 当时调name_new时存进去的这个名字对应的值。
- names表会按name比name_substr多的字符个数排序,和name_substr越是接近的越排前面。暂时写死了只返回15个
- names (table) - 一个数组。数组的每一项有两个字段:
每次调用都会去数据库搜索
-- 获取索引是'nick'的这类名字里面,包含substr的名字
sce.s.name_search('nick', substr,
{
ok = function (names)
-- 查询完成
end,
error = function (code, reason)
-- 查询失败
end,
timeout = function ()
-- 查询超时
end,
})
list_add
列表新增项
- 参数
- player (player/integer/nil) - 玩家
- key (string) - 列表的key
- value (string/integer/table/nil) - 统计的值
local c = sce.s.get_commit()
c.list_add(player, key, value)
...
c.commit("列表新增")
list_modify
列表修改项
- 参数
- player (player/integer/nil) - 玩家
- key (string) - 列表的key
- list_id (integer) - list项的唯一标识id
- value (string/integer/table/nil) - 目标value
local c = sce.s.get_commit()
c.list_modify(player, key, list_id, value)
...
c.commit("列表修改值")
list_delete
列表删除项
- 参数
- player (player/integer/nil) - 玩家
- key (string) - 列表的key
- list_id (integer) - list项的唯一标识id。注意每一项的id都是独立和互不干扰的,删除某一项不会对别的项的id有变化
local c = sce.s.get_commit()
c.list_delete(player, key, list_id)
...
c.commit("列表删除")
list_query
列表查询。返回的数据按时间的逆序排序。即晚插入的排前面。
由c++实现的api
- 参数
- map_name (string) - 要查询的地图名
- player (player/integer/nil) - 玩家
- key (string) - 列表的key
- limit (integer) - (可选) 返回最多limit条记录。不填则表示返回所有
- callback (table) - 和其它查询类api的callback类似,具体写法参考例子
- timetype (string) - (可选) 如果这里传一个字符串
stamp
,则返回的time
是一个以秒为单位的时间戳,类似1587524484
这样。否则(默认情况),返回的是一个时间,类似2020-04-22 11:01:24
- 返回
- user_id (integer) - 玩家id
- key (string) - 统计的key
- value (string) - 统计的值
- list_id (integer) - list项的唯一标识id。注意id会是个比较大的数,id不能通过加减来得到别的id
- time (string) - 列表被插入的时间(或时间戳)。不管是时间戳还是时间,这里都是字符串类型。
sce.s.list_query(player, key, {
ok = function (result)
-- 查询完成
if #result > 0 then
for i, u in ipairs(result) do
print(u.user_id)
print(u.key)
print(u.value)
print(u.list_id)
end
end
end,
})
sce.s.list_query(player, key, {
ok = function (result)
-- 查询完成
if #result > 0 then
for i, u in ipairs(result) do
print(u.user_id)
print(u.key)
print(u.value)
print(u.list_id)
end
end
end,
}, 'stamp')
item
物品相关,现阶段用来做玩家背包的
query_item
查询用户所拥有的所有或者某一key类型的物品,user_id和item_id相同或者user_id,key,expire_type,expire_time相同都会被认为是同一物品
由c++实现的api
- 参数
- player (player/integer) - 玩家
- callback (table) - 查询回调
- key (string) - 可选参数,表示查询某一类物品
- 返回
- user_id (integer) - 玩家id
- key (string) - 物品分类key
- item_name (string) - 物品名
- expire_type (integer) - 过期类型 0-永久,1-指定日期过期,2-指定时间过期
- expire_time (string) - 过期时间,都是指定日期,指定时间入库时也换算成指定日期,对于永久类型的过期时间一定为9999-12-31 23:59:59
- count (integer) - 物品数量
- value (table) - 物品额外信息
- item_id (integer) - 物品id
sce.s.query_item(player, {
ok = function (result)
-- 查询完成
if #result > 0 then
for i, u in ipairs(result) do
print(u.user_id)
print(u.key)
print(u.item_name)
print(u.expire_type)
print(u.expire_time)
print(u.count)
print(u.value)
print(u.item_id)
end
end
end,
}, 'key')
item_add
添加物品
- 参数
- player (player/integer) - 玩家
- key (string) - 标识某一类物品
- item_name (string) - 物品名
- count (integer) - 物品数量
- value (table) - 物品额外信息
- expire_type (integer) - 过期类型 0-永久,1-指定日期过期,2-指定时间过期
- expire_time (string) - 过期时间,都是指定日期,指定时间入库时也换算成指定日期,对于永久类型的过期时间一定为9999-12-31 23:59:59, 永久类型不需要填此参数
local c = sce.s.get_commit()
c.item_add(player, 'key', 'item_name0', 10, {}, 0)
c.item_add(player, 'key', 'item_name1', 10, {}, 1, '2020-3-4 20:12:12')
c.item_add(player, 'key', 'item_name2', 10, {}, 2, 1*60*60) // 一小时后过期,单位秒
c.commit('item add')
item_use
使用物品
- 参数
- player (player/integer) - 玩家
- item_id (integer) - 物品id
- count (integer) - 物品数量
local c = sce.s.get_commit()
c.item_use(player, item_id, 8)
c.commit('item use')